﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace UtilZ.Dotnet.Ex.Model
{
#pragma warning disable 8500

    /// <summary>
    /// 数组内存池管理器
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    public class ArrayPoolZManager<T>
    {
        private readonly BufferSearchAlgorithmType _searchAlgorithmType;
        private readonly ArrayPoolZ<T>[] _memoryPools;

        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="opions">选项集合</param>
        /// <param name="searchAlgorithmType">搜索算法类型,选项集合中的项少于5项时推荐顺序查找,多于5项时建议二分查找</param>
        public ArrayPoolZManager(IEnumerable<MemoryPoolZOption> opions, BufferSearchAlgorithmType searchAlgorithmType = BufferSearchAlgorithmType.Binary)
        {
            if (opions == null)
            {
                throw new ArgumentNullException(nameof(opions));
            }

            if (opions.Count() <= 0)
            {
                throw new ArgumentException("集合不能为空.");
            }

            this._searchAlgorithmType = searchAlgorithmType;
            opions = opions.Distinct().OrderBy(t => { return t.BlockAlignedSize; });
            this._memoryPools = new ArrayPoolZ<T>[opions.Count()];
            for (int i = 0; i < this._memoryPools.Length; i++)
            {
                var opion = opions.ElementAt(i);
                this._memoryPools[i] = new ArrayPoolZ<T>(opion.BlockAlignedSize, opion.IncrementBlocks);
            }
        }

        /// <summary>
        /// 租借指定长度的内存块
        /// </summary>
        /// <param name="dataLength">数据长度</param>
        /// <returns>租借到的内存块</returns>
        public IArrayBlock<T> Rent(int dataLength)
        {
            if (this._searchAlgorithmType == BufferSearchAlgorithmType.Sequence)
            {
                return SequenceRent(this._memoryPools, dataLength);
            }
            else
            {
                return BinaryRent(this._memoryPools, dataLength);
            }
        }

        private static IArrayBlock<T> SequenceRent(ArrayPoolZ<T>[] memoryPools, int dataLength)
        {
            foreach (var memoryPool in memoryPools)
            {
                if (memoryPool.BlockAlignedSize >= dataLength)
                {
                    return memoryPool.Rent(dataLength);
                }
            }

            throw new InvalidOperationException($"获取容量\"{dataLength}\"不在初始化范围内");
        }

        private static IArrayBlock<T> BinaryRent(ArrayPoolZ<T>[] memoryPools, int dataLength)
        {
            int left = 0, right = memoryPools.Length - 1, midIndex;
            ArrayPoolZ<T> memoryPool;

            while (left <= right)
            {
                midIndex = left + ((right - left) >> 1);
                memoryPool = memoryPools[midIndex];

                if (memoryPool.BlockAlignedSize >= dataLength)
                {
                    return memoryPool.Rent(dataLength);
                }
                else if (memoryPool.BlockAlignedSize < dataLength)
                {
                    left = midIndex + 1;
                }
                else
                {
                    right = midIndex - 1;
                }
            }

            throw new InvalidOperationException($"获取容量\"{dataLength}\"不在初始化范围内");
        }

        /// <summary>
        /// 释放资源,释放后不能再使用已租借的内存块
        /// </summary>
        public void Dispose()
        {
            for (int i = 0; i < this._memoryPools.Length; i++)
            {
                this._memoryPools[i].Dispose();
                this._memoryPools[i] = null;
            }
        }
    }

    /// <summary>
    /// 数组内存池
    /// </summary>
    /// <typeparam name="T">数据类型</typeparam>
    unsafe public class ArrayPoolZ<T>
    {
        /// <summary>
        /// 内存块对齐大小
        /// </summary>
        public readonly int BlockAlignedSize;

        /// <summary>
        /// 内存扩增时的单次增加内存块数
        /// </summary>
        public readonly int IncrementBlocks;

        private readonly ConcurrentBag<ArrayBlock> _blocks = new ConcurrentBag<ArrayBlock>();
        private readonly object _lock = new object();
        private bool _dsposabled = false;


        /// <summary>
        /// 构造函数初始化
        /// </summary>
        /// <param name="blockAlignedSize">内存块对齐大小</param>
        /// <param name="incrementBlocks">内存扩增时的单次增加内存块数</param>
        public ArrayPoolZ(int blockAlignedSize, int incrementBlocks)
        {
            this.BlockAlignedSize = blockAlignedSize;
            this.IncrementBlocks = incrementBlocks;
        }


        /// <summary>
        /// 租借指定长度的数据块
        /// </summary>
        /// <param name="dataLength">数据长度</param>
        /// <returns>租借到的内存块</returns>
        public IArrayBlock<T> Rent(int dataLength)
        {
            if (this._dsposabled)
            {
                throw new ObjectDisposedException(nameof(ArrayPoolZ<T>), "当前对象已释放");
            }

            ArrayBlock block;
            if (this._blocks.TryTake(out block))
            {
                block.SetLength(dataLength);
                return block;
            }

            lock (this._lock)
            {
                while (true)
                {
                    if (this._dsposabled)
                    {
                        throw new ObjectDisposedException(nameof(ArrayPoolZ<T>), "当前对象已释放");
                    }

                    this.AddMemoryPointer();
                    if (this._blocks.TryTake(out block))
                    {
                        block.SetLength(dataLength);
                        return block;
                    }
                }
            }
        }

        private void AddMemoryPointer()
        {
            for (int i = 0; i < this.IncrementBlocks; i++)
            {
                this._blocks.Add(new ArrayBlock(this.BlockAlignedSize, this));
            }
        }


        /// <summary>
        /// 返回租借的内存块
        /// </summary>
        /// <param name="block">用于返回的租借的内存块</param>
        internal void Return(IArrayBlock<T> block)
        {
            this._blocks.Add((ArrayBlock)block);
        }

        /// <summary>
        /// 释放资源,释放后不能再使用已租借的内存块
        /// </summary>
        public void Dispose()
        {
            lock (this._lock)
            {
                if (this._dsposabled)
                {
                    return;
                }
                this._dsposabled = true;

                this._blocks.Clear();
            }
        }

        /// <summary>
        /// 数据块
        /// </summary>
        private class ArrayBlock : IArrayBlock<T>
        {
            private readonly ArraySpan<T> _data;
            private readonly ArrayPoolZ<T> _owner;

            public ArrayBlock(int capcity, ArrayPoolZ<T> poolRegion)
            {
                this._data = new ArraySpan<T>(new T[capcity]);
                this._owner = poolRegion;
            }

            internal void SetLength(int length)
            {
                if (length > this._data.Length)
                {
                    throw new ArgumentOutOfRangeException(nameof(length), $"数据长度{length}超出容量范围{this._data.Length}.");
                }

                this._data.Update(0, length);
            }

            /// <summary>
            /// 数据
            /// </summary>
            public ArraySpan<T> Data
            {
                get
                {
                    return this._data;
                }
            }

            public void Dispose()
            {
                this._owner.Return(this);
            }
        }
    }


    /// <summary>
    /// 数组数据块
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IArrayBlock<T> : IDisposable
    {
        /// <summary>
        /// 数据
        /// </summary>
        ArraySpan<T> Data { get; }
    }

}
